﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfDrawing
{
    /// <summary>
    /// PieChartControl.xaml 的交互逻辑
    /// </summary>
    public partial class PieChartControl : UserControl
    {

        public string Title { get; set; }
        public List<PiePart> PieParts { get; set; }

        public double Radius { get; set; }

        public PieChartControl()
        {
            InitializeComponent();
            lbTitle.Content = "2019年消费比列";
            piechart.Radius = 100;

            PieParts = new List<PiePart>()
            {
                new PiePart(){ PartName="吃饭", Percent=0.35,Fill=Brushes.Red},
                new PiePart(){ PartName="逛街", Percent=0.15,Fill=Brushes.Blue},
                new PiePart(){ PartName="买衣服", Percent=0.10,Fill=Brushes.Green},
                new PiePart(){ PartName="买车", Percent=0.40,Fill=Brushes.Gray},

            };
            

            lst.ItemsSource = PieParts;
            piechart.DrawPileChart(PieParts,90);
        }

        private void root_Loaded(object sender, RoutedEventArgs e)
        {
            
        }
    }

    public class PieChart : FrameworkElement
    {

        public List<DrawingVisual> LstChild = new List<DrawingVisual>();

        public double Radius { get; set; }

        public Point CenterPoint {
            get
            {
                return new Point(Radius + 20, Radius + 20);
            }
        }
        private int OffsetAngle=0;//第一块的初始偏移角度，默认正北为0


        private Point FirstPoint //相对起始点为 坐标0,radius   偏移OffsetAngle的点
        {
            get
            {
                return GetCirclePointFromPoint(new Point(0, -Radius), OffsetAngle,out Point txtp);
            }
        }

        public Point GetRealPoint(Point RelitivePoint)
        {
            var tmp = new Point(RelitivePoint.X + CenterPoint.X, RelitivePoint.Y + CenterPoint.Y);
            return tmp;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="currentPoint">当前的相对坐标</param>
        /// <param name="bs"></param>
        /// <param name="ane"></param>
        /// <returns>返回下一个起始相对坐标</returns>
        private Point DrawOneBlock(Point currentPoint, PiePart piePart)
        {
            DrawingVisual _drawingVisual = new DrawingVisual();
            this.AddVisualChild(_drawingVisual);
            LstChild.Add(_drawingVisual);

            PathGeometry pg = new PathGeometry();

            PathFigure pathFigure = new PathFigure() { IsClosed = true, IsFilled = true, StartPoint = CenterPoint };
            pathFigure.Segments.Add(new LineSegment(GetRealPoint(currentPoint), true));

            Point pointEnd = GetCirclePointFromPoint(currentPoint, piePart.Percent*360, out Point txtp);
            pathFigure.Segments.Add(new ArcSegment(GetRealPoint(pointEnd), new Size(Radius, Radius), 0, piePart.Percent*360 > 180 ? true : false, SweepDirection.Clockwise, true));
            pg.Figures.Add(pathFigure);
            var dc = _drawingVisual.RenderOpen();
            dc.DrawGeometry(piePart.Fill, new Pen(piePart.Fill, 3), pg);
            FormattedText formattedText = new FormattedText(
            string.Format("{0:P}", piePart.Percent),
            CultureInfo.GetCultureInfo("en-us"),
            FlowDirection.LeftToRight,
            new Typeface("Verdana"),
            10,
            Brushes.Black);
            dc.DrawText(formattedText, GetRealPoint(txtp));
            dc.Close();



            return pointEnd;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="PieParts"></param>
        /// <param name="pOffsetAngle">第一块的初始偏移角度，默认正北为0</param>
        public void DrawPileChart(List<PiePart> PieParts,int pOffsetAngle=0)
        {

            if(Radius <= 0)
            {
                return;
            }
            Point last= new Point();
            OffsetAngle = pOffsetAngle;

            foreach(var pie in PieParts)
            {
                if(PieParts.IndexOf(pie)==0)
                {
                    last = DrawOneBlock(FirstPoint, pie);
                    continue;
                }
                last = DrawOneBlock(last, pie);
            }
            
            

        }



        //必须重载这两个方法，不然是画不出来的
        protected override int VisualChildrenCount
        {
            get { return LstChild.Count; }
        }
        protected override Visual GetVisualChild(int index)
        {
            return LstChild[index];

        }





        /// <summary>
        /// 获取圆周上相对与某点 旋转 指定角度的点坐标,    Matrix需要使用center是（0，0）的相对坐标
        /// </summary>
        /// <param name="angle">角度,从0到360度，以正北方向为0度，顺时针旋转角度增加</param>
        /// <returns>在圆周上旋转角度后的坐标</returns>
        /// 重要，本段代码对Matrix的使用是复杂化了，不要学习，有空我再修改
        public Point GetCirclePointFromPoint(Point StartPoint, double angle,out Point textPoint)
        {

            var textPointVector= new Vector(StartPoint.X/2, StartPoint.Y/2);
            var textPointMatrix = new Matrix();
            textPointMatrix.Rotate(angle/2);
            var textPointmp = (textPointVector * textPointMatrix);
            textPoint= textPointmp+ new Point(-20, 0);

            // 圆心平移到原点后0度所对应的向量
            var zeroAngleVector = new Vector(StartPoint.X, StartPoint.Y);

            // 旋转角度所对应的矩阵
            var rotateMatrix = new Matrix();
            rotateMatrix.Rotate(angle);

            var tmp = (zeroAngleVector * rotateMatrix);
            // 因旋转的中心点在原点,最后需要平移到实际坐标上

            return tmp + new Point(0, 0);
        }
    }


    public class PiePart
    {
        public string PartName { get; set; }
        public double Percent { get; set; }
        public Brush Fill { get; set; }

    }




    public class HighLowItems
    {
        public int value;
        public string descirble;
    }

    public class HighLowLine : FrameworkElement
    {

        public List<DrawingVisual> LstChild = new List<DrawingVisual>();


        public HighLowLine()
        {
            Items = new List<HighLowItems>()
            {
                new HighLowItems(){value=0,descirble="2020-06-19 13:18:44.690" },
                new HighLowItems(){value=1,descirble="2020-06-19 13:18:44.690" },
                new HighLowItems(){value=0,descirble="2020-06-19 13:18:44.690" },
                new HighLowItems(){value=1,descirble="2020-06-19 13:18:44.690" },
                new HighLowItems(){value=0,descirble="2020-06-19 13:18:44.690" },
                new HighLowItems(){value=0,descirble="2020-06-19 13:18:44.690" },
            };
            DrawValueLine();
        }


        //必须重载这两个方法，不然是画不出来的
        protected override int VisualChildrenCount
        {
            get { return LstChild.Count; }
        }
        protected override Visual GetVisualChild(int index)
        {
            return LstChild[index];

        }


        public double SingleSegumentWidth { get; set; } = 150;
        public double YHigh = 100;
        public double YLow = 200;
        public double YText = 250;


        private double XCurrent = 0;
        public double XNext = 100;

        public List<HighLowItems> Items { get; set; }

        private double GetNextX()
        {
            //XNext += SingleSegumentWidth;
            XCurrent = XNext;
            XNext += SingleSegumentWidth;
            return XCurrent;
        }
        private double GetNextY(HighLowItems item)
        {
            return item.value == 0 ? YLow : YHigh;
        }

        private void AddNextLinePoint(PathFigure pathFigure, HighLowItems pre, HighLowItems cur)
        {
            double x = GetNextX();
            double y = GetNextY(cur);
            Point curpoint = new Point(x, y);

            if (pre.value== cur.value)
            {
                pathFigure.Segments.Add(new LineSegment(curpoint, true));
            }else
            {
                Point curpoint2 = new Point(x, y==YLow? YHigh:YLow);
                pathFigure.Segments.Add(new LineSegment(curpoint2, true));
                pathFigure.Segments.Add(new LineSegment(curpoint, true));
            }

        }

        private void DrawText(DrawingContext dc,string text,double x)
        {
            FormattedText formattedText = new FormattedText(text,
                                        CultureInfo.GetCultureInfo("en-us"),
                                        FlowDirection.LeftToRight,
                                        new Typeface("Verdana"),
                                        10,
                                        Brushes.Black);
            dc.DrawText(formattedText, new Point(x, YText));
        }
        private void DrawValueLine()
        {
            if(Items==null || Items.Count==0)
            {
                return;
            }

            DrawingVisual _drawingVisual = new DrawingVisual();
            this.AddVisualChild(_drawingVisual);
            LstChild.Add(_drawingVisual);
            var dc = _drawingVisual.RenderOpen();
            PathGeometry pg = new PathGeometry();


            Point StartPoint=new Point(GetNextX(),GetNextY(Items[0]));
            PathFigure pathFigure = new PathFigure() { IsClosed = false, IsFilled = false, StartPoint = StartPoint };
            DrawText(dc, Items[0].descirble, StartPoint.X);



            for (int i=1;i<Items.Count();i++)
            {
                AddNextLinePoint(pathFigure, Items[i - 1], Items[i]);
                DrawText(dc, Items[i].descirble, XCurrent);

            }
            pg.Figures.Add(pathFigure);

            dc.DrawGeometry(Brushes.Black, new Pen(Brushes.Red, 3), pg);
            dc.Close();
        }
    }


}
